#include <arpa/inet.h>  // for inet_pton
#include <errno.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>

#include <cstdlib>
#include <fstream>
#include <sstream>
#include <thread>

#include "nlohmann/json.hpp"
#include "npu.h"
#include "sat.h"
#include "shttpd.h"
#include "global.h"

// ------------------------ 常量定义 ------------------------
// constexpr char kWarningAreaConfigPath[] = "../data/WarningArea.json";
constexpr char kWarningAreaConfigPath[] = "../data/WarningAreaSetting.json";
constexpr int kMaxNonRecognitionAreas = 1;          // 最大警告区域数量
constexpr int kHttpPort = 8088;             // HTTP服务端口
constexpr char kAliasUri[] = "download";     // HTTP别名URI
constexpr char kAliasDir[] = "../data/";     // HTTP别名目录
constexpr char kModelPath[] = "../models/ub2024-04-10-01-720p.om";  // 模型路径
// constexpr char kModelPath[] = "../models/0510_yvu.om";  // 模型路径


// ------------------------ 类型别名 ------------------------
using Json = nlohmann::json;
using Vector2D = Vector;  // 

// ------------------------ 全局变量（建议封装，此处保持原有结构） ------------------------
NPU npu;
struct sockaddr_in srv_addr;  
int socket_fd;              
extern std::vector<NonRecognitionAreaAttr> NoRecognitionAreaAttrs;  

// ------------------------ 函数声明 ------------------------

bool ParseWarningAreaConfig();
void DrawWarningAreaRegion(RGN& rgn,bool isDraw);
void HandleDrawWarningAreaRequest(shttpd_arg arg);
void HandleWarningAreaBoxSetting(shttpd_arg arg);
void web_thread();
void send_msg_to_server(bbox_t bbox);



/**
 * @brief 从JSON文件解析警告区域配置
 * @return 解析成功返回true，否则返回false
 */
// bool ParseWarningAreaConfig(){
//     try {
//         std::ifstream config_file(kWarningAreaConfigPath);
//         if (!config_file.is_open()) {
//             std::cerr << "Failed to open config file: " << kWarningAreaConfigPath << std::endl;
//             return false;
//         }

//         Json config_json;
//         config_file >> config_json;
//         NoRecognitionAreaAttrs.clear();  // 清空原有数据

//         // 解析所有警告区域
//         for (uint8_t area_idx = 0; area_idx < kMaxNonRecognitionAreas; ++area_idx) {
//             char area_key[30];
//             snprintf(area_key, sizeof(area_key), "NonRecognizeArea_%d", area_idx + 1);

//             NonRecognitionAreaAttr area_attr;
//             // 解析顶点
//             uint8_t point_count = config_json.at(area_key).at("pointArr_Length");
//             for (uint8_t point_idx = 0; point_idx < point_count; ++point_idx) {
//                 float x = config_json.at(area_key).at("pointArr").at(point_idx).at("x");
//                 float y = config_json.at(area_key).at("pointArr").at(point_idx).at("y");
//                 area_attr.poly.Vertices.emplace_back(Vector2D(x, y));  // 使用emplace_back避免临时对象
//             }
//             // 解析警告标志位
//             area_attr.wp = config_json.at(area_key).at("wp");  // 对应保存时的键名
//             NoRecognitionAreaAttrs.push_back(area_attr);
//         }
//         return true;
//     } catch (const std::exception& e) {
//         std::cerr << "Parse config failed: " << e.what() << std::endl;
//         return false;
//     }
// }

bool ParseWarningAreaConfig() {
    try {
        std::ifstream config_file(kWarningAreaConfigPath);
        if (!config_file.is_open()) {
            std::cerr << "Failed to open config file: " << kWarningAreaConfigPath << std::endl;
            return false;
        }

        Json config_json;
        config_file >> config_json;

        NoRecognitionAreaAttrs.clear();  // 清空原有数据

        if (!config_json.contains("WarningArea")) {
            std::cerr << "Missing 'WarningArea' field in JSON." << std::endl;
            return false;
        }

        const auto& warning_area = config_json.at("WarningArea");

        NonRecognitionAreaAttr area_attr;

        // 解析顶点
        uint8_t point_count = warning_area.at("pointArr_Length");
        for (uint8_t point_idx = 0; point_idx < point_count; ++point_idx) {
            double x = warning_area.at("pointArr").at(point_idx).at("x");
            double y = warning_area.at("pointArr").at(point_idx).at("y");

            area_attr.poly.Vertices.emplace_back(Vector2D(x, y));
        }

        // 解析警告标志位
        area_attr.wp = warning_area.at("wp");

        // 存入全局变量
        NoRecognitionAreaAttrs.push_back(area_attr);

        return true;
    } catch (const std::exception& e) {
        std::cerr << "Parse config failed: " << e.what() << std::endl;
        return false;
    }
}


/**
 * @brief 绘制或清除警告区域
 * @param region 绘制区域对象
 * @param is_draw 是否绘制（true=绘制，false=清除）
 */

void DrawWarningAreaRegion(RGN& rgn,bool isDraw){
    std::vector<Polygon> noRecognitionPolys;
    for(auto NonRecognitionAreaAttr:NoRecognitionAreaAttrs){
        noRecognitionPolys.push_back(NonRecognitionAreaAttr.poly);
    }
    if(isDraw)
        rgn.draw_NonRecognitionPoly(1,noRecognitionPolys);
    else
        rgn.clear_NonRecognitionPoly(noRecognitionPolys);
}



// ------------------------ HTTP服务相关 ------------------------
/**
 * @brief 处理前端绘制警告区域的请求（HTTP回调）
 * @param arg shttpd请求参数
 */
void HandleDrawWarningAreaRequest(shttpd_arg arg) {
    const char* request_method = shttpd_get_env(arg, "REQUEST_METHOD");
    const char* request_uri = shttpd_get_env(arg, "REQUEST_URI");
    const char* query_string = shttpd_get_env(arg, "QUERY_STRING");

    char switch_value[5] = {0};  // 初始化为0，避免未初始化风险
    shttpd_get_var("switch", query_string, strlen(query_string), switch_value, sizeof(switch_value));

    static RGN warning_region;  // 静态区域对象（根据业务需求调整生命周期）
    bool is_draw = (strcmp(switch_value, "Q") != 0);  // "Q"表示清除，其他表示绘制
    std::cout << "is_drwa = " << is_draw << std::endl;
    if (is_draw) {
        isDetectionAreaDisabled = true;  // 禁用检测区域（避免误操作）
        std::cout << "isDetectionAreaDisabled = " << isDetectionAreaDisabled <<  std::endl;
    }
    else {
        isDetectionAreaDisabled = false;  // 解除禁用检测区域
        std::cout << "isDetectionAreaDisabled = " << isDetectionAreaDisabled <<  std::endl;
    }
    DrawWarningAreaRegion(warning_region, is_draw);

    // 响应HTTP请求
    shttpd_printf(arg, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n");
    shttpd_printf(arg, "设置成功");
    arg->flags |= SHTTPD_END_OF_OUTPUT;
}

/**
 * @brief Web服务线程函数（启动HTTP服务器）
 */
void web_thread(){
    int data = -15;
    shttpd_ctx_t *ctx = shttpd_init(0, 0);
    // 设置端口（需要字符串类型）
    shttpd_set_option(ctx, "ports", std::to_string(kHttpPort).c_str());
    // 构造别名参数（使用预定义常量）
    std::string alias_option = std::string(kAliasUri) + "=" + kAliasDir;
    shttpd_set_option(ctx, "aliases", alias_option.c_str());
    shttpd_set_option(ctx, "index_files",  "../data/index.html");
    shttpd_register_uri(ctx, "/HandleDrawWarningAreaRequest",&HandleDrawWarningAreaRequest, (void *) &data);//绘制警告区域
    shttpd_register_uri(ctx, "/WarningAreaSetting", &HandleWarningAreaBoxSetting, (void*)&data); // 接收不侦测区域点坐标


    while(true) {
        shttpd_poll(ctx, 10000);
    }
    shttpd_fini(ctx);
}

void HandleWarningAreaBoxSetting(shttpd_arg arg) {
    const char* query_string = shttpd_get_env(arg, "QUERY_STRING");

    char json_param[8192] = {0};
    if (shttpd_get_var("json", query_string, strlen(query_string), json_param, sizeof(json_param)) <= 0) {
        shttpd_printf(arg, "HTTP/1.0 400 Bad Request\r\nContent-Type: text/plain\r\n\r\n");
        shttpd_printf(arg, "Missing 'json' parameter");
        arg->flags |= SHTTPD_END_OF_OUTPUT;
        return;
    }

    try {
        Json config = Json::parse(json_param);
        std::cout << "[INFO] 接收到区域设置配置: " << config.dump(2) << std::endl;

        // 1. 保存 JSON 文件
        std::ofstream fout(kWarningAreaConfigPath);
        fout << config.dump(4);
        fout.close();

        // 2. 重新解析区域
        if (!ParseWarningAreaConfig()) {
            throw std::runtime_error("解析警告区域配置失败");
        }

        // 3. 重新绘制（注意：static 保证区域生命周期一致）
        static RGN warning_region;
        DrawWarningAreaRegion(warning_region, true);

        // 4. 返回 HTTP 成功响应
        shttpd_printf(arg, "HTTP/1.0 200 OK\r\nContent-Type: text/plain\r\n\r\n");
        shttpd_printf(arg, "区域设置成功并已更新");
    } catch (const std::exception& e) {
        shttpd_printf(arg, "HTTP/1.0 500 Internal Server Error\r\nContent-Type: text/plain\r\n\r\n");
        shttpd_printf(arg, "JSON解析失败: %s", e.what());
    }

    arg->flags |= SHTTPD_END_OF_OUTPUT;
}

void send_msg_to_server(bbox_t bbox)
{
    char buf[10] = {0};
    buf[8] = 0x0D;
    buf[9] = 0x0A;

    buf[0] = static_cast<uint16_t>(bbox.x >> 8);  // x
    buf[1] = static_cast<uint16_t>(bbox.x >> 0);
    buf[2] = static_cast<uint16_t>(bbox.y >> 8);  // y
    buf[3] = static_cast<uint16_t>(bbox.y >> 0);
    buf[4] = static_cast<uint16_t>(bbox.w >> 8);  // w
    buf[5] = static_cast<uint16_t>(bbox.w >> 0);
    buf[6] = static_cast<uint16_t>(bbox.h >> 8);  // h
    buf[7] = static_cast<uint16_t>(bbox.h >> 0);
    
    sendto(socket_fd, buf, 10, 0,(struct sockaddr*)&srv_addr,sizeof(srv_addr)); 
}

int main(int argc, char **argv)
{
    std::cout<<"--------- Client started ----------"<<std::endl;
    std::thread WebThread(web_thread);  //开启web线程
    WebThread.detach();
    
    // 加载不识别区域配置
    if (!ParseWarningAreaConfig()) {
        std::cerr << "Fatal: Failed to load non-recognition area config" << std::endl;
    }

    bool yvu_model = true;
    if (argc == 2)
    {
        if (strcmp(argv[1], "bgr") == 0)
        {
            yvu_model = false;
        }
    }

    socket_fd = socket(PF_INET, SOCK_DGRAM, 0);
    if (socket_fd == -1) exit(-1);
    
    bzero(&srv_addr, sizeof(srv_addr));
    srv_addr.sin_family = AF_INET;
    srv_addr.sin_port = htons(UDP_SERVER_PORT);
    srv_addr.sin_addr.s_addr = inet_addr(UDP_SERVER_IP);
    struct timeval timeout;
    timeout.tv_sec = 1;
    timeout.tv_usec = 0;
    if (setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == -1) {
        printf("setsockopt failed\n");
    }
    
    npu.Init(kModelPath, yvu_model);
    
    while (true) {
        bbox_t bbox={0};
        npu.NPUInvoke_client(bbox, SPY_SCORE_THRESHOLD);
        send_msg_to_server(bbox);     
    }
    
    close(socket_fd);
    return 0;	
}
